package com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.web;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;
import java.util.zip.ZipInputStream;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageInfo;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.enums.CommonOperateLogEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.FlowExecutionQueryService;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.FlowFileHandleService;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.FlowProcessService;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.MyApplyOperateService;
import com.iwhalecloud.citybrain.flow.platform.manager.common.ErrorCode;
import com.iwhalecloud.citybrain.flow.platform.manager.common.ResponseResult;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.*;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.log.CommonOperateLogContentVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.log.CommonOperateLogForAddVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.task.FlowExecutionQueryVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.task.TaskInfoVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowCommonOperateLogRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowFormRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.UserRepository;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.FormService;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.common.impl.identity.Authentication;
import org.flowable.engine.repository.DeploymentBuilder;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.form.model.FormField;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

/**
 * @author lilh
 * @date 2019-01-20 17:38
 */
@RestController
@RequestMapping("/process")
@Api
public class FlowProcessController {

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private ProcessEngine processEngine;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private FlowProcessService flowProcessService;

    @Autowired
    private FlowExecutionQueryService flowExecutionQueryService;

    @Autowired
    private FlowCommonOperateLogRepository flowCommonOperateLogRepository;

    @Autowired
    private FormService formService;

    @Autowired
    private FlowFormRepository flowFormRepository;

    @Autowired
    private MyApplyOperateService myApplyOperateService;

    @Autowired
    private FlowFileHandleService fileHandleService;



    /**
     * 待处理
     */
    @ApiOperation(value = "待处理")
    @PostMapping("/todo")
    public ResponseResult todoTasks(@RequestBody FlowExecutionQueryVo flowExecutionQueryVo) {
        PageInfo<FlowExecutionQueryInfo> pageInfos = new PageInfo<>(flowExecutionQueryService.todoTasks(flowExecutionQueryVo));
        return ResponseResult.success(pageInfos.getList()).setTotalCount((int) pageInfos.getTotal())
                .setPageNum(pageInfos.getPageNum()).setPageSize(pageInfos.getPageSize());
    }

    /**
     * 已处理
     */
    @ApiOperation(value = "已处理")
    @PostMapping("/processed")
    public ResponseResult processed(@RequestBody FlowExecutionQueryVo flowExecutionQueryVo) {
        PageInfo<FlowExecutionQueryInfo> pageInfos = new PageInfo<>(flowExecutionQueryService.processedProcessInstance(flowExecutionQueryVo));
        return ResponseResult.success(pageInfos.getList()).setTotalCount((int) pageInfos.getTotal())
                .setPageNum(pageInfos.getPageNum()).setPageSize(pageInfos.getPageSize());



    }

    /**
     * 我的申请
     */
    @ApiOperation(value = "我的申请")
    @PostMapping("/apply")
    public ResponseResult apply(@RequestBody FlowExecutionQueryVo flowExecutionQueryVo) {
        PageInfo<FlowExecutionQueryInfo> pageInfos = new PageInfo<>(flowExecutionQueryService.launchedProcessInstance(flowExecutionQueryVo));
        return ResponseResult.success(pageInfos.getList()).setTotalCount((int) pageInfos.getTotal())
                .setPageNum(pageInfos.getPageNum()).setPageSize(pageInfos.getPageSize());
    }

    /**
     * 催办
     */
    @ApiOperation(value = "催办")
    @PostMapping("/remind")
    public ResponseResult remind(@RequestBody @Validated(ProcessHandleVo.RemindGroup.class) ProcessHandleVo processHandleVo) {
        return ResponseResult.success(myApplyOperateService.remind(processHandleVo.getProcessInstanceId()));
    }

    /**
     * 撤回
     *
     * @param processHandleVo 信息
     * @return 结果
     */
    @ApiOperation(value = "撤回")
    @PostMapping("/recall")
    public ResponseResult recall(@RequestBody @Validated(ProcessHandleVo.RecallGroup.class) ProcessHandleVo processHandleVo) {
        return ResponseResult.success(myApplyOperateService.recall(processHandleVo.getProcessInstanceId(), processHandleVo.getComment()));
    }

    /**
     * 获取启动表单
     *
     * @param processDefinitionKey 流程编码
     * @return 启动表单信息
     * （司疆）修改，增加了外置表单的需求
     */
    @ApiOperation(value = "获取启动表单")
    @GetMapping(value = "/start")
    public ResponseResult start(String processDefinitionKey) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(processDefinitionKey).latestVersion()
                .singleResult();
        if (processDefinition == null) {
            throw new NullPointerException("流程定义不存在");
        }
        //如果流程设置了开始表单
        if (processDefinition.hasStartFormKey()) {
            //获取到流程设定的开始表单编码
            String formKey = formService.getStartFormKey(processDefinition.getId());
            return ResponseResult.success(flowFormRepository.findDeployForm(formKey));
        }

        return ResponseResult.success();
    }


    @ApiOperation(value = "获取启动表单明细")
    @GetMapping("/getStartFormDetail")
    public ResponseResult getStartFormDetail(String processInstanceId) {
        if(StringUtils.isEmpty(processInstanceId)){
            return ResponseResult.success();
        }
        //内置表单
        return ResponseResult.success(flowFormRepository.findStartFormDetail(processInstanceId));

    }


    /**
     * 开始流程
     * (20210715 司疆修改增加了对外置表单的适配)
     * @param
     * @return 流程实例id
     */
    @ApiOperation(value = "开始流程")
    @PostMapping("/start")
    public ResponseResult start(@RequestBody ProcessStartVo processStartVo) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(processStartVo.getProcessDefinitionKey())
                .active()
                .latestVersion()
                .singleResult();
        if (Objects.isNull(processDefinition)) {
            return ResponseResult.fail(ErrorCode.NOT_EXIST.errCode(), "流程" + processStartVo.getProcessDefinitionKey() + "不存在");
        }
        User current = userRepository.getCurrentUser();
        if (Objects.isNull(current)) {
            return ResponseResult.fail(ErrorCode.NOT_EXIST.errCode(), "查询当前用户失败");
        }

        //设置启动流程人员
        Authentication.setAuthenticatedUserId(current.getUserCode());
        try {

            //如果为内置表单
            if(StringUtils.isEmpty(processStartVo.getFormId())){
                Map<String,Object> variables = processStartVo.getVariables();
                return ResponseResult.success(flowProcessService.startProcess(processStartVo.getProcessDefinitionKey(), variables));
            }
            //如果为外置表单
            return ResponseResult.success(flowProcessService.startProcessWithExternalForm(processStartVo.getProcessDefinitionKey(),processStartVo.getFormId()));
        } catch (Exception e) {
            logErrorToDb(processStartVo, processDefinition, e.getLocalizedMessage(), current);
            return ResponseResult.fail(888, e.getLocalizedMessage());
        }

    }

    private List<FormField> convert(List<Object> fields) {
        return JSONObject.parseArray(JSONObject.toJSONString(fields), FormField.class);
    }

    private void logErrorToDb(ProcessStartVo processStartVo, ProcessDefinition processDefinition, String localizedMessage, User current) {
        CommonOperateLogContentVo content = new CommonOperateLogContentVo();
        content.setRequest(processStartVo);
        content.setResponse(localizedMessage);
        CommonOperateLogForAddVo addVo = new CommonOperateLogForAddVo();
        addVo.setType(CommonOperateLogEnum.START_APPLICATION.getCode());
        addVo.setModelKey(processDefinition.getKey());
        addVo.setCreateBy(current.getUserName());
        flowCommonOperateLogRepository.insertOperateLog(addVo, content);
    }

    /**
     * 上传文件部署
     */
    @PostMapping(value = "/upload")
    public ResponseResult deploy(MultipartFile file) {

        try {
            InputStream fileInputStream = file.getInputStream();
            String fileName = file.getOriginalFilename();
            String extension = FilenameUtils.getExtension(fileName);
            String name = FilenameUtils.getBaseName(fileName);
            DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
            deploymentBuilder.name(name);
            String zipFileSuffix = "zip";
            String barFileSuffix = "bar";
            if (Arrays.asList(zipFileSuffix, barFileSuffix).contains(extension)) {
                ZipInputStream zip = new ZipInputStream(fileInputStream);
                deploymentBuilder.addZipInputStream(zip).deploy();
            } else {
                deploymentBuilder.addInputStream(fileName, fileInputStream).deploy();
            }
        } catch (Exception e) {
            ResponseResult.fail(1000);
        }
        return ResponseResult.success();
    }/**/

    /**
     * 生成流程图
     *
     * @param processId 流程实例id
     */
    @ApiOperation(value = "生成流程图")
    @GetMapping(value = "/diagram/{processId}")
    public void genProcessDiagram(HttpServletResponse httpServletResponse, @PathVariable String processId) throws Exception {
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();

        //流程走完的不显示图
        if (pi == null) {
            return;
        }
        Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
        //使用流程实例ID，查询正在执行的执行对象表，返回流程实例对象
        String instanceId = task.getProcessInstanceId();
        List<Execution> executions = runtimeService
                .createExecutionQuery()
                .processInstanceId(instanceId)
                .list();

        //得到正在执行的Activity的Id
        List<String> activityIds = new ArrayList<>();
        List<String> flows = new ArrayList<>();
        for (Execution exe : executions) {
            List<String> ids = runtimeService.getActiveActivityIds(exe.getId());
            activityIds.addAll(ids);
        }

        //获取流程图
        BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
        ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
        ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
        InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds, flows, engconf.getActivityFontName(), engconf.getLabelFontName(), engconf.getAnnotationFontName(), engconf.getClassLoader(), 1.0);
        OutputStream out = null;
        byte[] buf = new byte[1024];
        int legth = 0;
        try {
            out = httpServletResponse.getOutputStream();
            while ((legth = in.read(buf)) != -1) {
                out.write(buf, 0, legth);
            }
        } finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
    }

    /**
     * 流程监控列表
     *
     * @return
     */
    @ApiOperation(value = "流程监控列表")
    @PostMapping("/list")
    public ResponseResult list(HttpServletRequest request, @RequestBody ProcessQueryVo processQueryVo) {
        //processQueryVo.setAppId(WebUtils.getAppId(request));
        //processQueryVo.setTimeStart(DateUtils.startTimeFormatter(processQueryVo.getTimeStart()));
        //processQueryVo.setTimeEnd(DateUtils.endTimeFormatter(processQueryVo.getTimeEnd()));
        return flowProcessService.list(processQueryVo);
    }


    /**
     * 流程实例详情-流程图与基本信息
     *
     * @param
     * @return
     */
    @ApiOperation(value = "流程图与基本信息")
    @PostMapping("/diagramAndInfo")
    public ResponseResult processInstanceDiagramAndInfo(@RequestBody ProcessQueryVo processQueryVo) {
        return flowProcessService.getProcessInstanceDiagramAndInfo(processQueryVo.getProcessInstanceId());
    }

    /**
     * 流程实例详情-当前任务列表
     *
     * @param
     * @return
     */
    @ApiOperation(value = "当前任务列表")
    @PostMapping("/currentTasks")
    public ResponseResult processInstanceCurrentTasks(@RequestBody ProcessQueryVo processQueryVo) {
        return flowProcessService.getProcessInstanceCurrentTasks(processQueryVo);
    }

    /**
     * 流程实例详情-处理日志
     *
     * @param
     * @return
     */
    @ApiOperation(value = "处理日志")
    @PostMapping("/handleLogs")
    public ResponseResult processInstanceHandleLogs(@RequestBody ProcessQueryVo processQueryVo) {
        return flowProcessService.getProcessInstanceHandleLogs(processQueryVo);
    }

    /**
     * 流程实例详情-会签任务信息
     *
     * @param
     * @return
     */
    @ApiOperation(value = "会签任务信息")
    @PostMapping("/mutiTaskInfo")
    public ResponseResult processInstanceMutiTaskInfo(@RequestBody @Validated TaskInfoVo taskInfoVo) {
        return flowProcessService.getProcessInstanceMutiTaskInfo(taskInfoVo.getTaskId(), taskInfoVo.getProcessInstanceId());
    }

    /**
     * 流程处理-挂起、激活、撤单、终止
     *
     * @return
     */
    @ApiOperation(value = "流程处理-挂起、激活、撤单、终止")
    @PostMapping("/handle")
    public ResponseResult processInstanceHandle(@RequestBody @Validated(ProcessHandleVo.MonitorGroup.class) ProcessHandleVo processHandleVo) {
        return flowProcessService.handleProcessInstance(processHandleVo);
    }

    /**
     * 获取附件列表信息
     */
    @ApiOperation(value = "获取附件列表信息")
    @PostMapping("/getAttachInfoList")
    public ResponseResult getAttachInfoList(@RequestBody FlowExecutionQueryVo flowExecutionQueryVo) {
        FlowModelFileResourceHandleVo handleRelVo = new FlowModelFileResourceHandleVo();
        handleRelVo.setProcessInstanceId(flowExecutionQueryVo.getProcessInstanceId());
        handleRelVo.setTaskId(flowExecutionQueryVo.getTaskId());
        handleRelVo.setServiceType(flowExecutionQueryVo.getServiceType());
        return ResponseResult.success(fileHandleService.selectByCondition(handleRelVo));
    }

    /**
     * 查询流程实例列表
     */
    @ApiOperation(value = "查询流程实例及实例各节点任务信息")
    @PostMapping("/getProcessInstanceList")
    public ResponseResult getProcessInstanceList(@RequestBody FlowExecutionQueryVo flowExecutionQueryVo) {
        PageInfo<FlowExecutionQueryInfo> pageInfos = new PageInfo<>(flowExecutionQueryService.getProcessInstanceList(flowExecutionQueryVo));
        return ResponseResult.success(pageInfos.getList()).setTotalCount((int) pageInfos.getTotal())
                .setPageNum(pageInfos.getPageNum()).setPageSize(pageInfos.getPageSize());
    }

    /**
     * 查询流程实例列表
     */
    @ApiOperation(value = "查询流程实例(一条实例只返回一条数据)")
    @PostMapping("/getSingleProcessInstanceList")
    public ResponseResult getSingleProcessInstanceList(@RequestBody FlowExecutionQueryVo flowExecutionQueryVo) {
        PageInfo<FlowExecutionQueryInfo> pageInfos = new PageInfo<>(flowExecutionQueryService.getSingleProcessInstanceList(flowExecutionQueryVo));
        return ResponseResult.success(pageInfos.getList()).setTotalCount((int) pageInfos.getTotal())
                .setPageNum(pageInfos.getPageNum()).setPageSize(pageInfos.getPageSize());
    }
}
